package com.lcj.odr.service.impl;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.codingapi.txlcn.tc.annotation.LcnTransaction;
import com.lcj.common.entity.OrderStatusEnum;
import com.lcj.common.exception.AochException;
import com.lcj.common.util.PageUtils;
import com.lcj.odr.controller.form.DriverIdForm;
import com.lcj.odr.controller.form.TransferForm;
import com.lcj.odr.db.dao.OrderBillDao;
import com.lcj.odr.db.dao.OrderDao;
import com.lcj.odr.db.dao.OrderProfitsharingDao;
import com.lcj.odr.db.pojo.OrderBillEntity;
import com.lcj.odr.db.pojo.OrderEntity;
import com.lcj.odr.feign.DrServiceApi;
import com.lcj.odr.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @Author : lcj
 * @create 2022/11/16 10:22
 */
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {

    public static final String REDIS_PREFIX_ORDER = "order#";

    @Resource
    private OrderDao orderDao;

    @Resource
    private OrderBillDao orderBillDao;

    @Resource
    private DrServiceApi drServiceApi;

    @Resource
    private OrderProfitsharingDao orderProfitsharingDao;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Override
    public PageUtils searchOrderAndCommentByPage(Map param) {
        long count = orderDao.searchOrderAndCommentCount(param);
        ArrayList<HashMap> list = null;
        if (count > 0) {
            list = orderDao.searchOrderAndCommentByPage(param);
        } else {
            list = new ArrayList<>();
        }
        int start = (Integer) param.get("start");
        int length = (Integer) param.get("length");
        PageUtils pageUtils = new PageUtils(list, count, start, length);
        return pageUtils;
    }

    @Override
    @Transactional
    @LcnTransaction
    public String updateOrderAboutPayment(Map param) {
        long orderId = MapUtil.getLong(param, "orderId");
        /*
         * 查询订单状态。
         */
        HashMap map = orderDao.searchUuidAndStatus(orderId);
        String uuid = MapUtil.getStr(map, "uuid");
        int status = MapUtil.getInt(map, "status");
        if (status == 7 || status == 8) {
            return "付款成功";
        }

        return "付款失败";
    }

    @Override
    public HashMap validCanPayOrder(Map param) {
        HashMap map = orderDao.validCanPayOrder(param);
        if (map == null || map.size() == 0) {
            throw new AochException("订单无法支付");
        }
        return map;
    }

    @Override
    @Transactional
    @LcnTransaction
    public int updateOrderPrepayId(Map param) {
        int rows = orderDao.updateOrderPrepayId(param);
        if (rows != 1) {
            throw new AochException("更新预支付订单ID失败");
        }
        return rows;
    }

    @Override
    public HashMap searchDriverTodayBusinessData(long driverId) {
       HashMap result = orderDao.searchDriverTodayBusinessData(driverId);
       if(MapUtil.getStr(result, "duration") == null){
           result.put("duration", "0");
       }
       if(MapUtil.getStr(result, "income") == null){
           result.put("income", "0.00");
       }
       return result;
    }

    @Override
    @Transactional
    @LcnTransaction
    public String insertOrder(OrderEntity orderEntity, OrderBillEntity billEntity) {
        //插入订单记录
        if(orderEntity.getCarPlate() == null) {
            orderEntity.setCarPlate("");
        }
        int rows = orderDao.insert(orderEntity);
        if (rows == 1) {
            String id = orderDao.searchOrderIdByUUID(orderEntity.getUuid());
            //插入订单费用记录
            billEntity.setOrderId(Long.parseLong(id));
            rows = orderBillDao.insert(billEntity);
            if (rows == 1) {
                //往Redis里面插入缓存，配合Redis事务用于司机抢单，避免多个司机同时抢单成功,15分钟30秒过期
                redisTemplate.opsForValue().set(REDIS_PREFIX_ORDER + id, "none",930, TimeUnit.SECONDS);
                //redisTemplate.expire(REDIS_PREFIX_ORDER + id, 15, TimeUnit.MINUTES);   //缓存15分钟
                return id;
            } else {
                throw new AochException("保存新订单费用失败");
            }
        } else {
            throw new AochException("保存新订单失败");
        }
    }

    @Override
    @Transactional
    @LcnTransaction
    public String acceptNewOrder(long driverId, long orderId) {
        //Redis不存在抢单的新订单就代表抢单失败
        if (!redisTemplate.hasKey(REDIS_PREFIX_ORDER + orderId)) {
            return "抢单失败";
        }
        //执行Redis事务
        redisTemplate.execute(new SessionCallback() {
            @Override
            public Object execute(RedisOperations operations) throws DataAccessException {
                //获取新订单记录的Version
                operations.watch(REDIS_PREFIX_ORDER + orderId);
                //本地缓存Redis操作
                operations.multi();
                //把新订单缓存的Value设置成抢单司机的ID
                operations.opsForValue().set(REDIS_PREFIX_ORDER + orderId, driverId+"");
                //执行Redis事务，如果事务提交失败会自动抛出异常
                return operations.exec();
            }
        });
        //抢单成功之后，删除Redis中的新订单，避免让其他司机参与抢单
        redisTemplate.delete(REDIS_PREFIX_ORDER + orderId);
        //更新订单记录，添加上接单司机ID和接单时间
        HashMap param = new HashMap();
        param.put("driverId", driverId);
        param.put("orderId", orderId);
        int rows = orderDao.acceptNewOrder(param);
        if (rows != 1) {
            throw new AochException("接单失败，无法更新订单记录");
        }
        return "接单成功";
    }


    @Override
    public HashMap searchDriverExecuteOrder(Map param) {
        HashMap map = orderDao.searchDriverExecuteOrder(param);
        return map;
    }

    @Override
    public Integer searchOrderStatus(Map param) {
        Integer status = orderDao.searchOrderStatus(param);
        if (status == null) {
            //throw new AochException("没有查询到数据，请核对查询条件");
            status = 0;
        }
        return status;
    }

    @Override
    @Transactional
    @LcnTransaction
    public String deleteUnAcceptOrder(Map param) {
        long orderId = MapUtil.getLong(param, "orderId");
        if (!redisTemplate.hasKey(REDIS_PREFIX_ORDER + orderId)) {
            return "订单取消失败";
        }
        redisTemplate.execute(new SessionCallback() {
            @Override
            public Object execute(RedisOperations operations) throws DataAccessException {
                operations.watch(REDIS_PREFIX_ORDER + orderId);
                operations.multi();
                operations.opsForValue().set(REDIS_PREFIX_ORDER + orderId, "none");
                return operations.exec();
            }
        });

        redisTemplate.delete(REDIS_PREFIX_ORDER + orderId);
        int rows = orderDao.deleteUnAcceptOrder(param);
        if (rows != 1) {
            return "订单取消失败";
        }
        rows = orderBillDao.deleteUnAcceptOrderBill(orderId);
        if(rows != 1) {
            return "账单取消失败";
        }
        return "订单取消成功";
    }

    @Override
    public HashMap searchDriverCurrentOrder(long driverId) {
        HashMap map = orderDao.searchDriverCurrentOrder(driverId);
        return map;
    }

    @Override
    public HashMap hasCustomerCurrentOrder(long customerId) {
        HashMap result = new HashMap();
        HashMap map = orderDao.hasCustomerUnAcceptOrder(customerId);
        result.put("hasCustomerUnAcceptOrder", map != null);
        result.put("unAcceptOrder", map);

        Long id = orderDao.hasCustomerUnFinishedOrder(customerId);
        result.put("hasCustomerUnFinishedOrder", id != null);
        result.put("unFinishedOrder", id);

        Long unpayedOrderId = orderDao.hasCustomerUnPayedOrder(customerId);
        result.put("hasCustomerUnPayedOrder", unpayedOrderId!=null);
        result.put("unPayedOrder", unpayedOrderId);
        return result;
    }

    @Override
    public HashMap searchOrderForMoveById(Map param) {
        HashMap map = orderDao.searchOrderForMoveById(param);
        if(OrderStatusEnum.ARRIVED.getCode().equals(MapUtil.getInt(map, "status"))) {
            String key = "order_driver_arrived#" + MapUtil.getStr(param, "orderId");
            if(redisTemplate.hasKey(key)) {
                map.put("confirmArrived", "2".equals(redisTemplate.opsForValue().get(key).toString()));
            }
        }
        return map;
    }

    @Override
    @Transactional
    @LcnTransaction
    public int arriveStartPlace(Map param) {
        //添加到达上车点标志位
        long orderId = MapUtil.getLong(param, "orderId");
        if(redisTemplate.hasKey("order_driver_arrived#" + orderId)
                && redisTemplate.opsForValue().get("order_driver_arrived#"+orderId).toString().equals("2")){
        } else {
            redisTemplate.opsForValue().set("order_driver_arrived#" + orderId, "1");
        }

        int rows = orderDao.updateOrderStatus(param);
        if (rows != 1) {
            throw new AochException("更新订单状态失败");
        }
        return rows;
    }

    @Override
    public boolean confirmArriveStartPlace(long orderId) {
        String key = "order_driver_arrived#" + orderId;
//        if (redisTemplate.hasKey(key) && redisTemplate.opsForValue().get(key).toString().endsWith("1")) {
//            redisTemplate.opsForValue().set(key, "2");
//            return true;
//        }
//        return false;
        redisTemplate.opsForValue().set(key, "2");
        return true;
    }

    @Override
    @Transactional
    @LcnTransaction
    public int startDriving(Map param) {
        long orderId = MapUtil.getLong(param, "orderId");
        String key = "order_driver_arrived#" + orderId;
        if (redisTemplate.hasKey(key) && redisTemplate.opsForValue().get(key).toString().endsWith("2")) {
            int rows = orderDao.updateOrderStatus(param);
            if (rows != 1) {
                throw new AochException("更新订单状态失败");
            }
            redisTemplate.delete(key);
            return rows;
        }
        return 0;
    }

    @Override
    @Transactional
    @LcnTransaction
    public int updateOrderStatus(Map param) {
        int rows = orderDao.updateOrderStatus(param);
        if (rows != 1) {
            throw new AochException("更新取消订单记录失败");
        }
        return rows;
    }

    @Override
    public PageUtils searchOrderByPage(Map param) {
        long count = orderDao.searchOrderCount(param);
        ArrayList<HashMap> list = null;
        if (count == 0) {
            list = new ArrayList<>();
        } else {
            list = orderDao.searchOrderByPage(param);
        }
        int start = (Integer) param.get("start");
        int length = (Integer) param.get("length");
        PageUtils pageUtils = new PageUtils(list, count, start, length);
        return pageUtils;
    }

    @Override
    public HashMap searchOrderContent(long orderId) {
        HashMap map = orderDao.searchOrderContent(orderId);
        JSONObject startPlaceLocation = JSONUtil.parseObj(MapUtil.getStr(map, "startPlaceLocation"));
        JSONObject endPlaceLocation = JSONUtil.parseObj(MapUtil.getStr(map, "endPlaceLocation"));

        map.replace("startPlaceLocation", startPlaceLocation);
        map.replace("endPlaceLocation", endPlaceLocation);
        return map;
    }

    @Override
    public ArrayList<HashMap> searchOrderStartLocationIn30Days() {
        ArrayList<String> list = orderDao.searchOrderStartLocationInDays();
        ArrayList<HashMap> result = new ArrayList<>();
        list.forEach(location -> {
            JSONObject json = JSONUtil.parseObj(location);
            String latitude = json.getStr("latitude");
            String longitude = json.getStr("longitude");
            latitude = latitude.substring(0, latitude.length() - 4);
            latitude += "0001";
            longitude = longitude.substring(0, longitude.length() - 4);
            longitude += "0001";
            HashMap map = new HashMap();
            map.put("latitude", latitude);
            map.put("longitude", longitude);
            result.add(map);
        });
        return result;
    }

    @Override
    public boolean validDriverOwnOrder(Map param) {
        long count = orderDao.validDriverOwnOrder(param);
        return count == 1 ? true : false;
    }

    @Override
    public HashMap searchSettlementNeedData(long orderId) {
        HashMap map = orderDao.searchSettlementNeedData(orderId);
        return map;
    }

    @Override
    public HashMap searchOrderById(Map param) {
        HashMap map = orderDao.searchOrderById(param);
        String startPlaceLocation=MapUtil.getStr(map,"startPlaceLocation");
        String endPlaceLocation=MapUtil.getStr(map,"endPlaceLocation");
        map.replace("startPlaceLocation",JSONUtil.parseObj(startPlaceLocation));
        map.replace("endPlaceLocation",JSONUtil.parseObj(endPlaceLocation));
        return map;
    }

    @Override
    public String searchOrderDriver(long orderId) {
        return orderDao.searchOrderDriver(orderId);
    }

    @Override
    @Transactional
    @LcnTransaction
    public void handlePayment(Long orderId, String payId, String driverOpenId, String payTime) {
        /*
         * 1等待接单，2已接单，3司机已到达，4开始代驾，5结束代驾，6未付款，7已付款，8订单已结束，9顾客撤单，10司机撤单，11事故关闭，12其他
         * 更新订单状态之前，先查询订单的状态。
         * 因为乘客端付款成功之后，会主动发起Ajax请求，要求更新订单状态。
         * 所以后端接收到付款通知消息之后，不要着急修改订单状态，先看一下订单是否已经是7状态
         */
        HashMap map = orderDao.searchOrderIdAndStatus(orderId);
        int status = MapUtil.getInt(map, "status");
        if (status == 7) {
            return;
        }

        HashMap param = new HashMap();
        param.put("id", orderId);
        param.put("payId", payId);
        param.put("payTime", payTime);
        //更新订单记录的PayId、状态和付款时间
        int rows = orderDao.updateOrderPayIdAndStatus(param);
        if (rows != 1) {
            throw new AochException("更新支付订单ID失败");
        }

        List<TransferForm>  transferList = new ArrayList<>();
        //查询系统奖励
        map = orderDao.searchDriverIdAndIncentiveFee(orderId);
        String incentiveFee = MapUtil.getStr(map, "incentiveFee");
        long driverId = MapUtil.getLong(map, "driverId");
        //判断系统奖励费是否大于0
        if (new BigDecimal(incentiveFee).compareTo(new BigDecimal("0.00")) == 1) {
            TransferForm form = new TransferForm();
            form.setUuid(IdUtil.simpleUUID());
            form.setAmount(incentiveFee);
            form.setDriverId(driverId);
            form.setType((byte) 2);
            form.setRemark("系统奖励费");
            //给司机钱包转账奖励费
            drServiceApi.transfer(form);
            transferList.add(form);
        }

        //执行分账
        //查询分账记录ID、分账金额
        map = orderProfitsharingDao.searchDriverIncome(orderId);
        if (map == null || map.size() == 0) {
            log.error("没有查询到分账记录");
            return;
        }
        String driverIncome = MapUtil.getStr(map, "driverIncome");
        long profitsharingId = MapUtil.getLong(map, "profitsharingId");

        if (new BigDecimal(driverIncome).compareTo(new BigDecimal("0.00")) == 1) {
            TransferForm form = new TransferForm();
            form.setUuid(IdUtil.simpleUUID());
            form.setAmount(driverIncome);
            form.setDriverId(driverId);
            form.setType((byte) 2);
            form.setRemark("订单："+orderId+"行程费");
            //给司机钱包转账分账费
            drServiceApi.transfer(form);
            //更新分账状态
            rows = orderProfitsharingDao.updateProfitsharingStatus(profitsharingId);
            if (rows != 1) {
                log.error("更新分账状态失败", new AochException("更新分账状态失败"));
            }
        }
        //更新订单状态为已完成状态（8）已完成
        rows = orderDao.finishOrder(orderId);
        if (rows != 1) {
            throw new AochException("更新订单结束状态失败");
        }

    }

    @Override
    public Long searchOrderWeekNumber(DriverIdForm form) {
        Map<String, Object> param = new HashMap();
        param.put("driverId", form.getDriverId());
        param.put("week", 1);
        long num = orderDao.searchOrderAndCommentCount(param);
        return num;
    }

    @Override
    public Long searchOrderTotalNumber(DriverIdForm form) {
        Map<String, Object> param = new HashMap();
        param.put("driverId", form.getDriverId());
        long num = orderDao.searchOrderAndCommentCount(param);
        return num;
    }

}
